import React, { useState } from 'react';
import { message, Select, Spin } from 'antd';
import debounce from 'lodash/debounce';
import type { DefaultOptionType, LabeledValue, SelectProps } from 'antd/es/select';

export type RemoteSelectItem = LabeledValue;

interface RemoteSelectProps<VT> extends SelectProps<VT> {
  fieldNames?: { key: string; value: string; label: string };
  antiShakeInterval?: number;
  request: (search?: string) => Promise<{ success?: boolean; data?: any[]; [key: string]: any }>;
}

/**
 * Select labelInValue 为真时 其 value 的类型为 {key: string, value:string, label: string}
 * 借鉴 Cascader 中 fieldNames 的写法 使其能够自定义 key,value,label 的字段名
 * @param fieldNames
 */
const valueConvertor = (
  fieldNames: { key: string; value: string; label: string },
  placeholder: React.ReactNode,
) => {
  const toRaw = (value?: any | any[]): LabeledValue | LabeledValue[] => {
    if (Array.isArray(value)) {
      return value.map((item) => ({
        key: item[fieldNames.key],
        value: item[fieldNames.value],
        label: item[fieldNames.label],
      }));
    }

    if (!value) {
      return {
        key: '',
        value: '',
        label: <div style={{ color: '#bfbfbf' }}>{placeholder}</div>,
      };
    }

    return {
      key: value[fieldNames.key],
      value: value[fieldNames.value],
      label: value[fieldNames.label],
    };
  };

  const toCustomer = (value?: LabeledValue | LabeledValue[]): any | any[] => {
    if (Array.isArray(value)) {
      return value.map((item) => ({
        [fieldNames.key]: item.key,
        [fieldNames.value]: item.value,
        [fieldNames.label]: item.label,
      }));
    }

    if (!value) {
      return {
        [fieldNames.key]: '',
        [fieldNames.value]: '',
        [fieldNames.label]: '',
      };
    }
    return {
      [fieldNames.key]: value.key,
      [fieldNames.value]: value.value,
      [fieldNames.label]: value.label,
    };
  };

  return {
    toRaw,
    toCustomer,
  };
};

const RemoteSelect: React.FC<RemoteSelectProps<LabeledValue>> = (props) => {
  const {
    fieldNames = { key: 'key', value: 'value', label: 'label' },
    antiShakeInterval = 500,
    request,
    defaultValue,
    value,
    placeholder,
    onDeselect,
    onSelect,
    onChange,
    ...selectProps
  } = props;
  const converter = valueConvertor(
    fieldNames,
    placeholder != '请输入' && placeholder != undefined ? placeholder : '请选择',
  );

  const [fetching, setFetching] = useState<boolean>(false);
  const [options, setOptions] = useState<any[]>([]);
  let lastFetchId: number = 0;

  const fetch = debounce(async (search?: string) => {
    lastFetchId += 1;
    const fetchId = lastFetchId;
    setFetching(true);
    const result = await request(search);

    setFetching(false);
    if (fetchId !== lastFetchId) {
      // for fetch callback order
      return;
    }
    if (result.code !== 0) {
      message.error(`获取失败`);
    } else {
      setOptions(result.data || []);
    }
  }, antiShakeInterval);
  converter.toRaw(value);
  return (
    <Select
      {...selectProps}
      labelInValue
      style={{ width: '65%' }}
      notFoundContent={fetching ? <Spin size="small" /> : null}
      // placeholder={placeholder != '请输入' && placeholder != undefined ? placeholder : '请选择'}
      showSearch
      getPopupContainer={(triggerNode) => triggerNode.parentElement} // 保证选择菜单不会随着页面滚动
      filterOption={false}
      onFocus={() => fetch()}
      onSearch={(search) => fetch(search)}
      defaultValue={converter.toRaw(defaultValue)}
      value={converter.toRaw(value)}
      onDeselect={(v: LabeledValue | LabeledValue[] | undefined, option: DefaultOptionType) =>
        onDeselect && onDeselect(converter.toCustomer(v), option)
      }
      onSelect={(v: LabeledValue | LabeledValue[] | undefined, option: DefaultOptionType) => {
        if (onSelect) {
          onSelect(converter.toCustomer(v), option);
        }
      }}
      onChange={(v, option) => {
        if (onChange) {
          onChange(converter.toCustomer(v), option);
        }
      }}
    >
      {options.map((item) => (
        <Select.Option key={item[fieldNames.value]} value={item[fieldNames.value]}>
          {item[fieldNames.label]}
        </Select.Option>
      ))}
    </Select>
  );
};

export default RemoteSelect;
